#include "kernel/apic/apic.h"
#include "libs/lib.h"
#include "kernel/log/log.h"
#include "kernel/smp/cpu.h"
#include "libs/msr.h"
#include "kernel/memory/memory.h"
#include "libs/io.h"






Apic::Apic(/* args */)
{
}

Apic::~Apic()
{
}

uint64_t Apic::Read(uint64_t regs){}
void Apic::Write(uint64_t regs,uint64_t value){}





localAPic::localAPic(/* args */)
{
    if(CPUMANAGER->cpufunction.apicSupport)
        LOG_INFO("HW SUPPORT APIC&XAPIC\t");
    else
        LOG_WARN("HW NOT SUPPORT APIC&XAPIC\t");

    if(CPUMANAGER->cpufunction.x2apicSupport)
        LOG_INFO("HW SUPPORT X2APIC\n");
    else
        LOG_WARN("HW NOT SUPPORT X2APIC\n");

    

    //开启全局x2APIC
    uint64_t temp =  rdmsr(IA32_APIC_BASE);
    baseAddress = temp & ~0x0fff;
    temp = temp | (1 << 10) | (1 << 11);
    wrmsr(IA32_APIC_BASE,temp);
    temp =  rdmsr(IA32_APIC_BASE);

    //软件开启APIC
    temp =  rdmsr(IA32_APIC_SVR);
    temp = temp | (1 << 8);
    temp |= IRQ_LOCALAPIC_BASE + IRQ_LOCALAPIC_SVR;
    // LOG_INFO("APIC SVR:%#018lx \t",temp);
    wrmsr(IA32_APIC_SVR,temp);
    
    temp =  rdmsr(IA32_APIC_SVR);
    if(temp & 0x100)
        LOG_INFO("SVR[8] enabled\n");


   

    //屏蔽所有LVT
    // temp = 0x10000;
    // wrmsr(IA32_APIC_CMCI,temp);
    // wrmsr(IA32_APIC_TIMER,temp);
    // wrmsr(IA32_APIC_TEMPRATURE,temp);
    // wrmsr(IA32_APIC_PERFORM,temp);
    // wrmsr(IA32_APIC_ERROR,temp);
    // wrmsr(IA32_APIC_LINT0,temp);
    // wrmsr(IA32_APIC_LINT1,temp);
    // LOG_INFO("mask all lvt\n");

    //开启错误处理中断
    // APIC_LVT  entry;
	// entry.vector = 0x20 + 19;
	// entry.deliver_status = APIC_ICR_IOAPIC_Idle;
	// entry.mask = APIC_ICR_IOAPIC_UN_Masked;

	// entry.deliver_mode = 0 ;
	// entry.timer_mode = 0;
	// entry.polarity = 0;
	// entry.irr = 0;
	// entry.trigger = 0;
	// entry.res_1 = 0;
	// entry.res_2 = 0;


    wrmsr(IA32_APIC_ERROR,IRQ_LOCALAPIC_BASE + IRQ_LOCALAPIC_ERROR);



    temp =  rdmsr(IA32_APIC_TPR);
    temp &= ~0xffu;
    wrmsr(IA32_APIC_TPR,temp);
    // LOG_INFO("set lvt tpr:%#010x\t",temp & 0xffffffff);
    temp =  rdmsr(IA32_APIC_PPR);
    // LOG_INFO("set lvt ppr:%#010x\n",temp & 0xffffffff);


    apicId =  rdmsr(IA32_APIC_ID) & 0xffffffff;

    LOG_INFO("apic id:%#10x\n",apicId);


}

localAPic::~localAPic()
{
}





IoApic::IoApic(/* args */)
{

}

IoApic::~IoApic()
{

}

void IoApic::enableRTE(uint64_t irq){
    
    uint64_t value = 0;
    irq = (irq - 32) * 2 + 0x10;
    value = Read(irq);
    value = value & (~0x10000UL);
    Write((irq),value);

}
void IoApic::disableRTE(uint64_t irq){

    uint64_t value = 0;
    irq = (irq - 32) * 2 + 0x10;
    value = Read(irq);
    value = value | (~0x10000UL);
    Write((irq),value);

}

void IoApic::install(uint64_t irq,void* arg){

    IO_APIC_RET_entry* entry = (IO_APIC_RET_entry*)arg;

    
    Write(0x10 + (irq - 32) * 2,*(uint64_t*)entry);
    

}
void IoApic::uninstall(uint64_t irq){

    Write((irq - 32) * 2 + 0x10,0x10000UL);

}

void IoApic::ack(uint64_t irq){

    wrmsr(IA32_APIC_EOI,0x00);

}




uint64_t IoApic::Read(uint64_t index){

    uint64_t ret;
    *virtual_index_address = index + 1;
    io_mfence();
    ret = *virtual_data_address;
    ret <<= 32;
    io_mfence();
    *virtual_index_address = index;
    io_mfence();
    ret |= *virtual_data_address;
    io_mfence();
    return ret;

}


void IoApic::Write(uint64_t index,uint64_t value){


     uint64_t ret;
    *virtual_index_address = index;
    io_mfence();
    *virtual_data_address = value & 0xffffffff;
    value >>=32;
    io_mfence();
   
    *virtual_index_address = index + 1;
    io_mfence();
    *virtual_data_address = value & 0xffffffff;
    io_mfence();

}


ApicManager* ApicManager::apicm = 0;

ApicManager::ApicManager(/* args */)
{

    uint32_t x;
	uint32_t * p;

    init8295a();
    //enable IMCR
    io_out8(0x22,0x70);
    io_out8(0x23,0x01);

	initLocalApic();


    initIoApic();

    // //get RCBA 芯片组 address
    // io_out32(PORT_PCI_CONTROL,0x80000f8f0);
    // x =io_in32(PORT_PCI_DATA);
    // // LOG_INFO("Get RCBA Address:%#010x\n",x);	
	// x = x & 0xffffc000;
	// // LOG_INFO("Get RCBA Address:%#010x\n",x);	

    // //get OIC address
	// if(x > 0xfec00000 && x < 0xfee00000)
	// {
	// 	p = (uint32_t *)Phy_To_Virt(x + 0x31feUL);
	// }

	// //set OIC,enable IOAPIC
	// x = (*p & 0xffffff00) | 0x100;
	// io_mfence();
	// *p = x;
	// io_mfence();
    apicm = this;
    sti();	
}

ApicManager::~ApicManager()
{



}

void ApicManager::init8295a(){


    	io_out8(0x21,0xff);
		io_out8(0xa1,0xff);

}


void ApicManager::initLocalApic(){



    // temp =  rdmsr(IA32_APIC_ID);
    // LOG_INFO("x2apic id:%#010x\t",temp & 0xffffffff);

    // temp =  rdmsr(IA32_APIC_VERSION);
    // LOG_INFO("local apic version:%#010x,max lvt entry:%#10x,svr(suppress eoi broadcast) \
            :%#04x\n",temp & 0xff,(temp >> 16 & 0xff) + 1,temp >> 24 & 0x1);
    // if((temp & 0xff) < 0x10)
        // LOG_INFO("8248DX discreate APIC\n");
    // else if(((temp & 0xff) < 0x10) && ((temp & 0xff) > 0x15))
        // LOG_INFO("integrated APIC\n");

    localAPic* temp =  new localAPic();
    localapics[temp->apicId] = temp;
    numOfLocalApic++;

}


void ApicManager::initIoApic(){

    uint64_t * tmp;
	unsigned char * IOAPIC_addr = (unsigned char *)Phy_To_Virt(0xfec00000);
    ioapic.physical_address = 0xfec00000;
    ioapic.virtual_index_address = IOAPIC_addr;
    ioapic.virtual_data_address = (uint32_t*)(IOAPIC_addr + 0x10);
    ioapic.virtual_EOI_address = (uint32_t*)(IOAPIC_addr + 0x40);


    uint64_t* ppml4e;
	uint64_t* ppdpte;
	uint64_t* ppdte;
	MEMORY->get_page_table_entry((uint64_t)IOAPIC_addr,&ppml4e,&ppdpte,&ppdte);

    if (*ppml4e == 0)
    {
        uint64_t* virtualAddress = (uint64_t*)MEMORY->MEMORY->kmalloc(PAGE_4K_SIZE, 0);
        set_mpl4t(ppml4e,mk_mpl4t(Virt_To_Phy(virtualAddress),PAGE_KERNEL_GDT));
    }

    if (*ppdpte == 0)
    {
        uint64_t* virtualAddress = (uint64_t*)MEMORY->MEMORY->kmalloc(PAGE_4K_SIZE, 0);
        set_pdpt(ppdpte,mk_pdpt(Virt_To_Phy(virtualAddress),PAGE_KERNEL_Dir));
    }
    

    uint64_t* virtualAddress = (uint64_t*)MEMORY->MEMORY->kmalloc(PAGE_4K_SIZE, 0);
    set_pdt(ppdte,mk_pdpt(ioapic.physical_address,PAGE_KERNEL_Page |PAGE_PWT | PAGE_PCD));

    flush_tlb();

    int i ;
	//	I/O APIC
	//	I/O APIC	ID	
	*ioapic.virtual_index_address = 0x00;
	io_mfence();
	*ioapic.virtual_data_address = 0x0f000000;
	io_mfence();
	LOG_INFO("Get IOAPIC ID REG:%#010x,ID:%#010x\n",*ioapic.virtual_data_address, *ioapic.virtual_data_address >> 24 & 0xf);
	io_mfence();

	//	I/O APIC	Version
	*ioapic.virtual_index_address = 0x01;
	io_mfence();
	LOG_INFO("Get IOAPIC Version REG:%#010x,MAX redirection enties:%#08d\n",*ioapic.virtual_data_address ,((*ioapic.virtual_data_address >> 16) & 0xff) + 1);

	//RTE	
	for(i = 0x10;i < 0x40;i += 2)
		ioapic.Write(i,0x10020 + ((i - 0x10) >> 1));

}